﻿using System;
using PocketEngine.Zips;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Tao.Glfw;
using Tao.OpenGl;
using Tao.DevIl;
using PocketEngine.Images;
using PocketEngine.Maths;
using PocketEngine.Particles;
using FastMath;
using PocketEngine.Physics;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using System.Speech;
using System.Speech.Recognition;
using System.Speech.Synthesis;
namespace PocketEngine.Engine
{
    public class pHost : MarshalByRefObject
    {
        string name = "Host";
        public System.Windows.Forms.Control HostControl;
        public pEngine Engine;
    }
    public class pSpeechProcessor
    {
        public virtual void UserSaid(string thesewords,float thissure,SpeechRecognizedEventArgs evt)
        {
            
        }
    }
    public class pSP_System : pSpeechProcessor
    {
        public override void UserSaid(string thesewords,float thissure,SpeechRecognizedEventArgs evt)
        {
            bool doExit = false;
            thesewords = thesewords.ToLower();
            Console.WriteLine("Said:" + thesewords);
            Console.WriteLine("Sure:" + thissure);
            if (thesewords == "exit")
            {
                doExit = true;
            }
            if(thesewords.Contains("exit") || thesewords.Contains("quit") || thesewords.Contains("terminate") || thesewords.Contains("abort"))
            {
                if(thesewords.Contains("leave") || thesewords.Contains("game") || thesewords.Contains("app") || thesewords.Contains("prog"))
                {
                    doExit = true;
                    
                }
            }
            if (doExit)
            {
                //pEngine.Speech.Say(pEngine.GoodbyeMessage);

                //Environment.Exit(-1);
            }
        }
    }
    public class pColor
    {
        public float R, G, B, A;
        
        public pColor()
        {
            R = G = B = A = 1;
        }
        
        public pColor(float r, float g, float b, float a)
        {
            R = r;
            G = g;
            B = b;
            A = a;
        }
        
        public void Bind()
        {
            Gl.glColor4f(R, G, B, A);
        }
        public pColor Blend(pColor w, float v)
        {
            return new pColor(R + (w.R - R) * v, G + (w.G - G) * v, B + (w.B - B) * v, A + (w.A - A) * v);
        }
        public static pColor White = new pColor(1, 1, 1, 1);
        public static pColor Black = new pColor(0, 0, 0, 1);
        public static pColor Red = new pColor(1, 0, 0, 1);
        public static pColor Green = new pColor(0, 1, 0, 1);
        public static pColor Blue = new pColor(0, 0, 1, 1);
        public static pColor LightBlue = new pColor(0, 1, 1, 1);

    }
   
    public class pEngine
    {
        public static pHost Host = null;
        public static string GoodbyeMessage = "Thank you for using Pocket Engine.";
        public static string WelcomeMessage = "Powered by Pocket Engine.";
        private static List<pSpeechProcessor> mSpeechHandlers = new List<pSpeechProcessor>();
        public static void AddSpeechProcessor(pSpeechProcessor sp)
        {
            mSpeechHandlers.Add(sp);
        }
        public class Speech
        {
            
            public static SpeechSynthesizer Synth = null;
            public static SpeechRecognitionEngine Recog = null;
            public static bool Init()
            {
                Synth = new SpeechSynthesizer();
                Recog = new SpeechRecognitionEngine();
                Recog.UnloadAllGrammars();
                Recog.SetInputToDefaultAudioDevice();
                Recog.SpeechRecognized+=new EventHandler<SpeechRecognizedEventArgs>(Recog_SpeechRecognized);
                
                return true;

            }
            public static void StartToListen()
            {
                Recog.RecognizeAsync(RecognizeMode.Multiple);
            }
            /// <summary>
            /// begins building the next phrase pattern
            /// </summary>
            public static void NewSpeechPattern()
            {
                gb = new GrammarBuilder(); 
            }
            public static void AddPhrase(string speech)
            {
                gb.Append(speech);
            }
            public static void AddChoice(params string[] choices)
            {
                gb.Append(new Choices(choices));
            }
            public static void FinishSpeechPattern()
            {
                var gr = new Grammar(gb);
                gr.Enabled = true;
                Recog.LoadGrammar(gr);
                gb = null;
            }
            private static GrammarBuilder gb;
            private static void Recog_SpeechRecognized(object sender, System.Speech.Recognition.SpeechRecognizedEventArgs e)
            {
                foreach (var sp in mSpeechHandlers)
                {
                    sp.UserSaid(e.Result.Text, e.Result.Confidence,e);
                }
                //Console.WriteLine(e.Result.Text + " (" + e.Result.Confidence.ToString() + ")");

            }

            public static void Say(string words)
            {
                Synth.SpeakAsync(words);
            }


        }
        public class Garbage
        {
            public static int[] texRef = new int[1024];
            public static int trC = 0;
            public static void AddTexture(int mGL)
            {
                texRef[trC] = mGL;
                trC++;
            }
            public static void Purge()
            {
                for (int i = 0; i < trC; i++)
                {
                    Gl.glDeleteTextures(1, ref texRef[trC]);
                }
            }
        }
        public class Particles
        {
            public static List<pParticle> Spawned = new List<pParticle>();
            public static void Spawn(pParticle template, pColor col, Vector3 pos, Vector3 inertia,float siz)
            {
                pParticle np = MakeParticle(template, col, ref inertia, siz, 1);
                if (np.Simulated)
                {
                    np.Entity.Body.Position = new pVec2(pos.X, pos.Y);
                    np.Entity.Body.Impulse(new pVec2(inertia.X, inertia.Y));

                }
                else
                {
                    np.Position = pos;
                }
                Spawned.Add(np);
               
            }
            public static void Spawn(pParticle template, pColor col, Vector3 pos, Vector3 inertia,float siz,float sizInc)
            {
                pParticle np = MakeParticle(template, col, ref inertia, siz, sizInc);
                if (np.Simulated)
                {
                    np.Entity.Body.Position = new pVec2(pos.X, pos.Y);
                    np.Entity.Body.Impulse(new pVec2(inertia.X, inertia.Y));

                }
                else
                {
                    np.Position = pos;
                }
                Spawned.Add(np);
            }
            public static void Update(Maps.pMap map)
            {
                if (map.OtherLights!=null)
                {
                    map.Lights = map.OtherLights;
                }
                map.OtherLights = map.Lights;
                map.Lights = new List<PocketEngine.Lights.pLight>();
                foreach (var l in map.OtherLights)
                {
                    map.Lights.Add(l);
                }
                foreach (var p in Spawned)
                {
                    p.Update(map);
                    if (p.CastLight)
                    {
                        Lights.pLight nl = new PocketEngine.Lights.pLight();
                        nl.LightRange = p.Size * 12;
                        nl.Position = new pVec3(p.Position.X, p.Position.Y, p.Position.Z);
                        nl.DiffuseColor = new pColor();
                        nl.DiffuseColor.R = p.Color.R * p.Alpha;
                        nl.DiffuseColor.G = p.Color.G * p.Alpha;
                        nl.DiffuseColor.B = p.Color.B * p.Alpha;
                        nl.DiffuseColor.A = 1;
                        map.Add(nl);

                    }
                }
                while (true)
                {
                    bool done = true;
                    foreach (var p in Spawned)
                    {
                        if(p.Alpha<=0.1)
                        {
                            Spawned.Remove(p);
                            done = false;
                            break;
                        }
                    }
                    if (done) break;
                }
            }
            public static void Draw(pImage back)
            {
                Effects.pEffectSoftParticle sp = BuiltInEffects.SoftParticle;
                Gl.glBlendFunc(Gl.GL_ONE, Gl.GL_ONE);
              //  back.Bind(1);
                foreach (var p in Spawned)
                {
                    if (p.CastLight) continue; 
                    sp.Color = p.Color;
                    sp.Bind(null);
                    p.Draw();
                    sp.Unbind(null);
                }
                //back.Unbind(1);
            }
            private static pParticle MakeParticle(pParticle template, pColor col, ref Vector3 inertia, float siz, float sizInc)
            {
                pParticle np = template.Clone(col);
                np.Inertia = inertia;
                np.Size = siz;
                np.SizeInc = sizInc;
                return np;
            }
        }

        public class BuiltInEffects
        {
            public static Effects.pEffectPointLight BasicPointLight;
            public static Effects.pEffectSoftParticle SoftParticle;
        }
        public static void Err(string reason,string location)
        {
            Console.WriteLine("Fatal Error:" + reason);
            Console.WriteLine("Location:" + location);
            while (true)
            {
            }
            Environment.Exit(-1);
        }
        public class Physics
        {
            public static PhysicsSimulator Sim = null;
            public static bool Init()
            {
                Sim = new PhysicsSimulator(new FarseerGames.FarseerPhysics.Mathematics.Vector2(0, 25));
                return true;
            }
            public static void Update(float time)
            {
                Sim.Update(time);
            }
            public static void AddBody(pBody body)
            {
                Sim.Add(body.Body);
            }
            public static void AddGeom(pGeom geom)
            {
                Sim.Add(geom.Geom);
            }

        }

        public class Input
        {
            public static bool GetMouseDown(int button)
            {
                int res = Glfw.glfwGetMouseButton(button);
                if (res>0)
                {
                    return true;
                }
                return false;

            }
            public static bool GetKeyDown(int key)
            {
                int res = Glfw.glfwGetKey(key);
                if (res > 0)
                {
                    return true;
                }
                return false;
            }
            public static pVec2 GetMousePos()
            {
                int mx, my;
                Glfw.glfwGetMousePos(out mx, out my);
                return new pVec2(mx, my);
            }
            public static void SetMousePos(pVec2 pos)
            {
                Glfw.glfwSetMousePos((int)pos.X, (int)pos.Y);
            }
            
        }
        public class Math
        {
            public static float PI = 3.14159265358979323846f;
            public static float DegRad(float x)
            {
                return x * PI / 180.0f;
            }
            public static float RadDeg(float x)
            {
                return x * 180.0f / PI;
            }
            public static pVec2 TransformPoint(pVec2 point, pVec2 origin, float angle, float scale)
            {
                double ang = (double)DegRad(angle);

                float px, py;
                px = point.X - origin.X;
                py = point.Y - origin.Y;


                float nx = (px * (float)System.Math.Cos(ang) + py * -(float)System.Math.Sin(ang)) * scale;
                float ny = (px * (float)System.Math.Sin(ang) + py * (float)System.Math.Cos(ang)) * scale;

                return new pVec2(origin.X+nx,origin.Y+ny);

            }
        }
        public class Pen
        {
            private static pVec2[] mVecs = new pVec2[4];

            public static void TransformedImage(int x, int y, int w, int h, pImage img, pColor col, float angle, float scale)
            {
                TransformedImageSquare(x, y, w, h, 0, 1, 1, 0, img, col, angle, scale);
            }
            public static void InjectShadowCoord(pVec2 pos)
            {
                float xo = pos.X / pEngine.Screen.Info.Width;
                float yo = pos.Y / pEngine.Screen.Info.Height;
                Gl.glMultiTexCoord2f(Gl.GL_TEXTURE1, xo, 1-yo);
            }
            public static void TransformedImageShadowMapping(int x, int y, int w, int h, pImage img, pColor col, float angle, float scale)
            {
                pVec2 origin = new pVec2(x + w / 2, y + h / 2);

                mVecs[0] = pEngine.Math.TransformPoint(new pVec2(x, y), origin, angle, scale);
                mVecs[1] = pEngine.Math.TransformPoint(new pVec2(x + w, y), origin, angle, scale);
                mVecs[2] = pEngine.Math.TransformPoint(new pVec2(x + w, y + h), origin, angle, scale);
                mVecs[3] = pEngine.Math.TransformPoint(new pVec2(x, y + h), origin, angle, scale);

                img.Bind(0);
                col.Bind();

                Gl.glBegin(Gl.GL_QUADS);

                Gl.glMultiTexCoord2f(Gl.GL_TEXTURE0, 0, 1);
                InjectShadowCoord(mVecs[0]);
                Gl.glVertex2f(mVecs[0].X, mVecs[0].Y);

                Gl.glMultiTexCoord2f(Gl.GL_TEXTURE0,1, 1);
                InjectShadowCoord(mVecs[1]);
                Gl.glVertex2f(mVecs[1].X, mVecs[1].Y);

                Gl.glMultiTexCoord2f(Gl.GL_TEXTURE0,1, 0);

                InjectShadowCoord(mVecs[2]);
                Gl.glVertex2f(mVecs[2].X, mVecs[2].Y);

                Gl.glMultiTexCoord2f(Gl.GL_TEXTURE0,0, 0);
                InjectShadowCoord(mVecs[3]);
                Gl.glVertex2f(mVecs[3].X, mVecs[3].Y);

                Gl.glEnd();

                img.Unbind(0);
            }
            public static void TransformedImageSquare(int x, int y, int w, int h,float u1,float v1,float u2,float v2, pImage img, pColor col, float angle, float scale)
            {

                pVec2 origin = new pVec2(x + w / 2, y + h / 2);

                mVecs[0] = pEngine.Math.TransformPoint(new pVec2(x, y), origin, angle, scale);
                mVecs[1] = pEngine.Math.TransformPoint(new pVec2(x + w, y), origin, angle, scale);
                mVecs[2] = pEngine.Math.TransformPoint(new pVec2(x + w, y + h), origin, angle, scale);
                mVecs[3] = pEngine.Math.TransformPoint(new pVec2(x, y + h), origin, angle, scale);

                img.Bind(0);
                col.Bind();
                Gl.glEnd();
                Gl.glBegin(Gl.GL_QUADS);

                Gl.glTexCoord2f(u1, v1);
                Gl.glVertex2f(mVecs[0].X, mVecs[0].Y);

                Gl.glTexCoord2f(u2, v1);
                Gl.glVertex2f(mVecs[1].X, mVecs[1].Y);

                Gl.glTexCoord2f(u2, v2);
                Gl.glVertex2f(mVecs[2].X, mVecs[2].Y);

                Gl.glTexCoord2f(u1, v2);
                Gl.glVertex2f(mVecs[3].X, mVecs[3].Y);

                Gl.glEnd();

                img.Unbind(0);

            }
            public static void Image(int x, int y, int w, int h, pImage img, pColor col)
            {
                
                img.Bind(0);
                RectUV(x, y, w, h, 0, 1, 1, 0, col);
                img.Unbind(0);
            }
            public static void ImageSquare(int x, int y, int w, int h, float u1, float v1, float u2, float v2, pImage img, pColor col)
            {
                img.Bind(0);
                RectUV(x, y, w, h, u1, v1, u2, v2, col);
                img.Unbind(0);
            }

            public static void RectSquareUV(int x, int y, int w, int h, pColor col)
            {

                RectUV(x, y, w, h, 0, 0, 1, 1, col);

            }
            public static void RectUV(int x, int y, int w, int h, float u1,float v1,float u2,float v2,pColor col)
            {
                Gl.glEnable(Gl.GL_BLEND);
                Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);

                col.Bind();

                Gl.glBegin(Gl.GL_QUADS);
                
                Gl.glTexCoord2f(u1, v1);
                Gl.glVertex2f(x, y);
               
                Gl.glTexCoord2f(u2, v1);
                Gl.glVertex2f(x + w, y);

                Gl.glTexCoord2f(u2, v2);
                Gl.glVertex2f(x + w, y + h);

                Gl.glTexCoord2f(u1,v2);
                Gl.glVertex2f(x,y+h);

                Gl.glEnd();
            }
            public static void Rect(int x, int y, int w, int h,pColor col)
            {
                col.Bind();
         
                Gl.glBegin(Gl.GL_QUADS);
                    Gl.glVertex2f(x, y);
                    Gl.glVertex2f(x + w, y);
                    Gl.glVertex2f(x + w, y + h);
                    Gl.glVertex2f(x, y + h);
                Gl.glEnd();
            }
        }
        public class Misc
        {
            public static bool Init()
            {
                Glfw.glfwInit();
                Il.ilInit();
                return true;
            }
        }
        public static string PrefabPassword = "EngineResources";
        public class Prefab
        {
            public static void MakePrefab(string templatePath, int tileWidth, int tileHeight, string outputZip,string comment)
            {
                if (new System.IO.FileInfo(templatePath).Exists == false)
                {
                    Console.WriteLine("Template could not be opened to convert into a prefab.");

                    return;
                }
                pZip output = new pZip(outputZip, comment);
                pImage img = new pImage(templatePath);

                var ds = img.ToStream();
                var ns = img.Normals.ToStream();
                var ss = img.Speculars.ToStream();

                int w = img.Width;
                int h = img.Height;

                int dx = 0;
                int dy = 0;
                int tx = 0;
                int ty = 0;
                int bx = -999;
                int by = -999;

                while (true)
                {

                    pImage block = GrabBlock(dx, dy, tileWidth, tileHeight,ds,ns,ss,img.Width,img.Height,img.BytesPerPixel);
                    output.Add("Tile-" + tx + "-" + ty, block,string.Empty);
                    if (tx > bx) bx = tx;
                    tx++;
                    dx += tileWidth;
                    if (dx >= img.Width)
                    {
                        tx = 0;
                        dx = 0;
                        ty++;
                        if (ty > by) by = ty;
                        dy += tileHeight;
                        if (dy >= img.Height)
                        {
                            break;
                        }

                    }
                }
                output.Add("Prefab.info", "Width(" + bx + ")Height(" + by + ")", comment);

                output.Save();
            }
            
           

        }
        public static int PIndex = 0;
        public static pImage GrabBlock(int x, int y, int w, int h, MemoryStream ds, MemoryStream ns, MemoryStream ss, int pw, int ph, int bpp)
        {
            pImage ret = _Grab(x, y, w, h, ds, pw, ph, bpp);
            ret.Normals = _Grab(x, y, w, h, ns, pw, ph, bpp);
            ret.Speculars = _Grab(x, y, w, h, ss, pw, ph, bpp);
            
           
            return ret;

        }
        public static pImage _Grab(int x, int y, int w, int h, MemoryStream s, int pw, int ph, int bpp)
        {
            byte[] buf = new byte[w * h * bpp];
            for (int dx = 0; dx < w; dx++)
            {
                for (int dy = 0; dy < h; dy++)
                {
                    int rx = dx + x;
                    int ry = dy + y;
                    int off = (dy * w * bpp) + dx * bpp;
                    int roff = (ry * pw * bpp) + rx * bpp;
                    if (rx > pw || ry > ph)
                    {
                        buf[off] = 0;
                        buf[off + 1] = 0;
                        buf[off + 2] = 0;
                        if (bpp > 3)
                        {
                            buf[off + 3] = 0;
                        }
                    }
                    else
                    {
                        s.Seek((long)roff, SeekOrigin.Begin);
                        buf[off] = (byte)s.ReadByte();
                        buf[off + 1] = (byte)s.ReadByte();
                        buf[off + 2] = (byte)s.ReadByte();
                        if (bpp > 3)
                        {
                            buf[off + 3] = (byte)s.ReadByte();
                        }
                    }
                }
            }
            pImageFormat form = pImageFormat.Rgb;
            if (bpp == 4)
            {
                form = pImageFormat.Rgba;
            }
            pImage ret = new pImage(buf, w, h, form);
            ret.Path = "Block(" + PIndex+")";
            PIndex++;
            return ret;
        }
        public class Screen
        {
            public static ScreenParameters Info = null;
            
            public static void ClearScreen()
            {
                Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
            }

            public static void SwapScreen()
            {
                Glfw.glfwSwapBuffers(); 
            }

            public static bool OpenSimpleWindow()
            {
                return OpenScreen(ScreenParameters.SimpleWindow());
            }

            public static bool OpenScreen(ScreenParameters parameters)
            {
                Info = parameters;
                if (Glfw.glfwOpenWindow(parameters.Width, parameters.Height, 8, 8, 8,8, 16, 0, Glfw.GLFW_WINDOW) == Gl.GL_FALSE)
                {
                    return false;
                }
                Glfw.glfwEnable(Glfw.GLFW_STICKY_KEYS);
                if (!parameters.VSync)
                {
                    DisableVSync();
                }
                Transform2D();
                SetupInternals();
                return true;

            }

            private static void SetupInternals()
            {
                pEngine.BuiltInEffects.BasicPointLight = new PocketEngine.Effects.pEffectPointLight();
                pEngine.BuiltInEffects.SoftParticle = new PocketEngine.Effects.pEffectSoftParticle();
                pEngine.Physics.Init();
                pEngine.Speech.Init();
                pEngine.Speech.Say(WelcomeMessage);
                Speech.NewSpeechPattern();
                Speech.AddPhrase("program");
                Speech.AddChoice("quit", "exit", "terminate", "abort");

                Speech.FinishSpeechPattern();
                Speech.NewSpeechPattern();
                Speech.AddPhrase("game");
                Speech.AddChoice("quit", "exit", "terminate", "abort");

                Speech.FinishSpeechPattern();
                Speech.NewSpeechPattern();
                Speech.AddPhrase("application");
                Speech.AddChoice("quit", "exit", "terminate", "abort");

                Speech.FinishSpeechPattern();
                Speech.NewSpeechPattern();
                Speech.AddPhrase("engine");
                Speech.AddChoice("quit", "exit", "terminate", "abort");

                Speech.FinishSpeechPattern();
                Speech.NewSpeechPattern();
                Speech.AddPhrase("exit");
                Speech.FinishSpeechPattern();

                AddSpeechProcessor(new pSP_System());
                Host = new pHost();
                Host.Engine = new pEngine();
                Gui.pGuiManager.InitGui();
            }
            public static void DisableVSync()
            {
                Glfw.glfwSwapInterval(0);
            }
            public static void InitControl(int w,int h)
            {
                Info = new ScreenParameters();
                Info.Width = w;
                Info.Height = h;
                Info.Bpp = 32;
                Info.VSync = false;
                Info.Windowed = false;
                Transform2D();
                SetupInternals();
            }
            public static void Transform2D()
            {
                Gl.glClearColor(0, 0.7f, 0.7f, 1);
                Gl.glViewport(0, 0, Info.Width, Info.Height);
                Gl.glDisable(Gl.GL_SCISSOR_TEST);
                Gl.glMatrixMode(Gl.GL_PROJECTION);
                Gl.glLoadIdentity();
                Glu.gluOrtho2D(0, Info.Width,0, Info.Height);
                Gl.glScaled(1, -1, 1);
                Gl.glTranslatef(0, -Info.Height, 0);
                Gl.glMatrixMode(Gl.GL_MODELVIEW);
                Gl.glLoadIdentity();
                Gl.glDisable(Gl.GL_SCISSOR_BIT);
                Gl.glDisable(Gl.GL_TEXTURE_2D);
                Gl.glDisable(Gl.GL_DEPTH_TEST);
                Gl.glDisable(Gl.GL_ALPHA_TEST);
                Gl.glEnable(Gl.GL_BLEND);
                Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
                
            }
        }
    }
    public class ScreenParameters
    {
        public int Width = 512;
        public int Height = 512;
        public int Bpp = 32;
        public bool Windowed = true;
        public int Frequency = 0;
        public bool VSync = false;
        public static ScreenParameters SimpleWindow()
        {
            return new ScreenParameters();
        }
    }
}
